package com.origin.utils.log

import android.content.Context
import android.location.Location
import android.util.Log
import com.origin.utils.BuildConfig
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.io.BufferedReader
import java.text.SimpleDateFormat
import java.util.*

class LogUtils private constructor() {


    init {
        throw UnsupportedOperationException("u can't instantiate me...")
    }

    companion object {

        private val V = 0x01
        private val D = 0x02
        private val I = 0x04
        private val W = 0x08
        private val E = 0x10
        private val A = 0x20
        private val JSON = 0xF2

        /**
         * 注意：BuildConfig 导包时一定要导入 当前 Module 的包，导入其他包 BuildConfig.DEBUG的值将会一直为false
         */

//        private val isLog = Util.isApkInDebug() // log总开关  发布release版本时， BuildConfig.DEBUG 为false;
        private val sGlobalTag: String = "" // log标签
        private val sTagIsSpace = true // log标签是否为空白
        private var isLogBorder = true // log边框开关

        private val TOP_BORDER =
            "┱┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄"
        private val LEFT_BORDER = "┃ "
        private val BOTTOM_BORDER =
            "┹┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄"
        private val LINE_SEPARATOR =
            System.getProperty("line.separator")//等同"\n"，没有 Windows和Linux的区别
        private const val MAX_LEN = 1024 //单条log打印长度限制为 4*1024个字符长度 网上多把这个值设置为4000，经测试发现还是会丢失内容
        private const val NULL_TIPS = "Log with null object."
        private val NULL = "null"
        private val ARGS = "args"

        /**
         * @param contents 不需要输入Tag  以类名为Tag
         */
        fun v(contents: Any, isShowStackTrace: Boolean = true, isSaveLog: Boolean = false, isCrash: Boolean = false) {
            log(
                V,
                sGlobalTag,
                isShowStackTrace,
                isSaveLog,
                isCrash,
                contents
            )
        }


        /**
         * @param tag      输入Tag
         * @param contents 打印内容
         */
        fun v(tag: String, vararg contents: Any, isShowStackTrace: Boolean = true, isSaveLog: Boolean = false, isCrash: Boolean = false) {
            log(
                V,
                tag,
                isShowStackTrace,
                isSaveLog,
                isCrash,
                *contents
            )
        }

        fun d(contents: Any, isShowStackTrace: Boolean = true, isSaveLog: Boolean = false, isCrash: Boolean = false) {
            log(
                D,
                sGlobalTag,
                isShowStackTrace,
                isSaveLog,
                isCrash,
                contents
            )
        }


        fun d(tag: String, vararg contents: Any, isShowStackTrace: Boolean = true, isSaveLog: Boolean = false, isCrash: Boolean = false) {
            log(
                D,
                tag,
                isShowStackTrace,
                isSaveLog,
                isCrash,
                *contents
            )
        }

        fun i(contents: Any, isShowStackTrace: Boolean = true, isSaveLog: Boolean = false, isCrash: Boolean = false) {
            log(
                I,
                sGlobalTag,
                isShowStackTrace,
                isSaveLog,
                isCrash,
                contents
            )
        }

        fun i(tag: String, vararg contents: Any, isShowStackTrace: Boolean = true, isSaveLog: Boolean = false, isCrash: Boolean = false) {
            log(
                I,
                tag,
                isShowStackTrace,
                isSaveLog,
                isCrash,
                *contents
            )
        }

        fun w(contents: Any, isShowStackTrace: Boolean = true, isSaveLog: Boolean = false, isCrash: Boolean = false) {
            log(
                W,
                sGlobalTag,
                isShowStackTrace,
                isSaveLog,
                isCrash,
                contents
            )
        }

        fun w(tag: String, vararg contents: Any, isShowStackTrace: Boolean = true, isSaveLog: Boolean = false, isCrash: Boolean = false) {
            log(
                W,
                tag,
                isShowStackTrace,
                isSaveLog,
                isCrash,
                *contents
            )
        }


        @JvmStatic
        fun __e(vararg contents: Any, isShowStackTrace: Boolean = false, isSaveLog: Boolean = false, isCrash: Boolean = false) {
            log(
                E,
                sGlobalTag,
                isShowStackTrace,
                isSaveLog,
                isCrash,
                *contents
            )
        }

        fun _e(vararg contents: Any, isShowStackTrace: Boolean = true, isSaveLog: Boolean = false, isCrash: Boolean = false) {
            log(
                E,
                sGlobalTag,
                isShowStackTrace,
                isSaveLog,
                isCrash,
                *contents
            )
        }

        fun e(tag: String, vararg contents: Any, isShowStackTrace: Boolean = true, isSaveLog: Boolean = false, isCrash: Boolean = false) {
            log(
                E,
                tag,
                isShowStackTrace,
                isSaveLog,
                isCrash,
                *contents
            )
        }

        fun a(contents: Any, isShowStackTrace: Boolean = true, isSaveLog: Boolean = false, isCrash: Boolean = false) {
            log(
                A,
                sGlobalTag,
                isShowStackTrace,
                isSaveLog,
                isCrash,
                contents
            )
        }

        fun a(tag: String, vararg contents: Any, isShowStackTrace: Boolean = true, isSaveLog: Boolean = false, isCrash: Boolean = false) {
            log(
                A,
                tag,
                isShowStackTrace,
                isSaveLog,
                isCrash,
                *contents
            )
        }

        fun crash(contents: String) {
            json(contents, true, true, true)
        }

        fun json(contents: String, isShowStackTrace: Boolean = true, isSaveLog: Boolean = false, isCrash: Boolean = false) {

            log(
                JSON,
                sGlobalTag,
                isShowStackTrace,
                isSaveLog,
                isCrash,
                contents
            )
        }

        fun json(tag: String, contents: String, isShowStackTrace: Boolean = true, isSaveLog: Boolean = false, isCrash: Boolean = false) {
            log(
                JSON,
                tag,
                isShowStackTrace,
                isSaveLog,
                isCrash,
                contents
            )
        }

        fun logFileUtilsInit() {
            LogFileUtils.initLogSavePath()
        }

        var isPrint = true


        private fun log(type: Int, tag: String, isShowStackTrace: Boolean = true, isSaveLog: Boolean = false, isCrash: Boolean = false, vararg contents: Any) {
//            if (!isCrash) {
//                if (!BuildConfig.DEBUG) {
//                    return
//                }
//            }
            if (!isPrint) {
                return
            }

            var mTag = tag
            val processContents =
                processContents(type, mTag, isShowStackTrace, *contents)


            mTag = processContents[0]
            val msg = processContents[1]

            when (type) {
                V, D, I, W, E, A -> printLog(
                    type,
                    mTag,
                    msg, isSaveLog, isCrash
                )
                JSON -> printLog(
                    D,
                    mTag,
                    msg, isSaveLog, isCrash
                )
            }

        }


        /**
         * @param type
         * @param tag
         * @param contents
         * @return
         */
        private fun processContents(type: Int, _tag: String, isShowStackTrace: Boolean = true, vararg contents: Any?): Array<String> {
            var tag = _tag

            //获取堆栈信息
            // 这里的数组的index=5是根据你工具类的层级做不同的定义，遍历之后就知道啦


            val targetIndex = 7

            val targetElement = Thread.currentThread().stackTrace
            val sb = StringBuffer()
            if (isShowStackTrace) {
                sb.append("\n\n执行流程\n")
                for (index in targetIndex until targetElement.size) {
                    val targetString = targetElement[index];
                    if (targetString.toString().contains("java.lang.reflect.Method.invoke")) {
                        break
                    }
                    sb.append(targetString).append("\n")
                }
            }


            val fileName = targetElement[targetIndex].fileName ?: "log日志.k"


            val mTag = if (fileName.indexOf(".") != -1) {
                fileName.substring(0, fileName.indexOf("."))
            } else {
                fileName
            }
            tag = if (isSpace(tag)) mTag else tag
            val headMsg = Formatter()//严格按（FileName:LineNuber）的格式来写 才可以定位
                .format(
                    "Thread: %s, %s`(%s:%d)" + LINE_SEPARATOR!!,
                    Thread.currentThread().name,
                    targetElement[targetIndex].methodName,
                    fileName,
                    targetElement[targetIndex].lineNumber
                )
                .toString()
            //打印内容部分
            var msg: String = NULL_TIPS
            if (contents.size == 1) {//单个msg
                val `object` = contents[0]
                msg = `object`?.toString() ?: NULL
                if (type == JSON) {
                    msg = formatJson(msg)
                }
            } else {//传入多个 “msg”
                val sb = StringBuilder()
                var i = 0
                val len = contents.size
                while (i < len) {
                    val content = contents[i]
                    sb.append(ARGS)
                        .append("[")
                        .append(i)
                        .append("]")
                        .append(" = ")
                        .append(content?.toString() ?: NULL)
                        .append(LINE_SEPARATOR)
                    ++i
                }
                msg = sb.toString()
            }
            msg += sb
            //拼接左边边宽
            if (isLogBorder) {
                val sb = StringBuilder()
                val lines = msg.split(LINE_SEPARATOR.toRegex()).dropLastWhile { it.isEmpty() }
                    .toTypedArray()
                for (line in lines) {
                    sb.append(line).append(LINE_SEPARATOR)
                }
                msg = sb.toString()

            }
            return arrayOf(tag, headMsg + msg)
        }


        /**
         * @param json 格式化json字符串
         * @return
         */
        private fun formatJson(_json: String): String {
            var json = _json
            //        Log.e("TAG", "formatJson: >>" + json);
            try {
                if (json.startsWith("{")) {
                    json = JSONObject(json).toString(4)
                } else if (json.startsWith("[")) {
                    json = JSONArray(json).toString(4)
                }
            } catch (e: JSONException) {
                e.printStackTrace()
            }

            return json
        }

        val ob = Any()

        /**
         * 打印完整日志
         *
         * @param type
         * @param tag
         * @param msg
         */
        private fun printLog(type: Int, tag: String?, msg: String, isSaveLog: Boolean, isCrash: Boolean) {
            synchronized(ob) {
                if (isLogBorder)
                //打印上边框
                    printBorder(type, tag, true, isSaveLog, isCrash)

                //防止 日志长度超限 导致日志信息打印不全
                val len = msg.length
                if (len > 0) {


                    val br = BufferedReader(msg.reader())

                    var s: String? = null
                    while (br.readLine().also { s = it } != null) { //使用readLine方法，一次读一行
                        printSubLog(type, tag, s!!, isSaveLog, isCrash)
                    }
                    br.close()

                } else {
                    printSubLog(type, tag, msg, isSaveLog, isCrash)
                }

                if (isLogBorder)
                //打印下边框
                    printBorder(type, tag, false, isSaveLog, isCrash)
            }

        }

        /**
         * 打印日志内容
         *
         * @param type
         * @param tag
         * @param msg
         */
        private fun printSubLog(type: Int, tag: String?, _msg: String, isSaveLog: Boolean, isCrash: Boolean) {
            var msg = _msg
            if (isLogBorder) {
                msg = LEFT_BORDER + msg
            }
            if (isSaveLog) {
                var fileName = getLogDate();
                if (isCrash) {
                    fileName = getLogDateTime();
                }
                LogFileUtils.writeLogFileExecutorService(
                    msg,
                    fileName, isCrash
                )
            }

            when (type) {
                V -> Log.v(tag, msg)
                D -> Log.d(tag, msg)
                I -> Log.i(tag, msg)
                W -> Log.w(tag, msg)
                E -> Log.e(tag, msg)
                A -> Log.wtf(tag, msg)
            }
        }

        /**
         * 打印日志边框
         *
         * @param type
         * @param tag
         * @param isTop
         */
        private fun printBorder(type: Int, tag: String?, isTop: Boolean, isSaveLog: Boolean, isCrash: Boolean) {
            val border = if (isTop) TOP_BORDER else BOTTOM_BORDER
            if (isSaveLog) {
                var fileName = getLogDate();
                if (isCrash) {
                    fileName = getLogDateTime();
                }
                LogFileUtils.writeLogFileExecutorService(
                    border,
                    fileName, isCrash
                )
            }


            when (type) {
                V -> Log.v(tag, border)
                D -> Log.d(tag, border)
                I -> Log.i(tag, border)
                W -> Log.w(tag, border)
                E -> Log.e(tag, border)
                A -> Log.wtf(tag, border)
            }
        }

        private fun isSpace(s: String?): Boolean {
            if (s == null) return true
            var i = 0
            val len = s.length
            while (i < len) {
                if (!Character.isWhitespace(s[i])) {
                    return false
                }
                ++i
            }
            return true
        }

        private fun getLogDate(): String {
            val sdf = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
            return sdf.format(Date())
        }

        private fun getLogDateTime(): String {
            val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
            return sdf.format(Date())
        }

        fun readLogText(filePathName: String): String {
            return LogFileUtils.readLogText(filePathName)
        }

    }


}

